// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef QUICHE_QUIC_MOQT_MOQT_SESSION_CALLBACKS_H_
#define QUICHE_QUIC_MOQT_MOQT_SESSION_CALLBACKS_H_

#include <optional>
#include <utility>

#include "absl/strings/string_view.h"
#include "quiche/quic/core/quic_clock.h"
#include "quiche/quic/core/quic_default_clock.h"
#include "quiche/quic/moqt/moqt_messages.h"
#include "quiche/common/quiche_callbacks.h"

namespace moqt {

// The callback we'll use for all request types going forward. Can only be used
// once; if the argument is nullopt, an OK response was received. Otherwise, an
// ERROR response was received.
using MoqtResponseCallback =
    quiche::SingleUseCallback<void(std::optional<MoqtRequestError>)>;

// Called when the SETUP message from the peer is received.
using MoqtSessionEstablishedCallback = quiche::SingleUseCallback<void()>;

// Called when a GOAWAY message is received from the server.
using MoqtSessionGoAwayCallback =
    quiche::SingleUseCallback<void(absl::string_view new_session_uri)>;

// Called when the session is terminated.
using MoqtSessionTerminatedCallback =
    quiche::SingleUseCallback<void(absl::string_view error_message)>;

// Called from the session destructor.
using MoqtSessionDeletedCallback = quiche::SingleUseCallback<void()>;

// Called whenever a PUBLISH_NAMESPACE or PUBLISH_NAMESPACE_DONE message is
// received from the peer. PUBLISH_NAMESPACE sets a value for |parameters|,
// PUBLISH_NAMESPACE_DONE does not..
using MoqtIncomingPublishNamespaceCallback = quiche::MultiUseCallback<void(
    const TrackNamespace& track_namespace,
    const std::optional<VersionSpecificParameters>& parameters,
    MoqtResponseCallback callback)>;

// Called whenever SUBSCRIBE_NAMESPACE or UNSUBSCRIBE_NAMESPACE is received from
// the peer. SUBSCRIBE_NAMESPACE sets a value for |parameters|,
// UNSUBSCRIBE_NAMESPACE does not. For UNSUBSCRIBE_NAMESPACE, |callback| is
// null.
using MoqtIncomingSubscribeNamespaceCallback = quiche::MultiUseCallback<void(
    const TrackNamespace& track_namespace,
    std::optional<VersionSpecificParameters> parameters,
    MoqtResponseCallback callback)>;

inline void DefaultIncomingPublishNamespaceCallback(
    const TrackNamespace&, const std::optional<VersionSpecificParameters>&,
    MoqtResponseCallback callback) {
  if (callback == nullptr) {
    return;
  }
  return std::move(callback)(MoqtRequestError{
      RequestErrorCode::kNotSupported,
      "This endpoint does not support incoming SUBSCRIBE_NAMESPACE messages"});
};

inline void DefaultIncomingSubscribeNamespaceCallback(
    const TrackNamespace& track_namespace,
    std::optional<VersionSpecificParameters>, MoqtResponseCallback callback) {
  std::move(callback)(std::nullopt);
}

// Callbacks for session-level events.
struct MoqtSessionCallbacks {
  MoqtSessionEstablishedCallback session_established_callback = +[] {};
  MoqtSessionGoAwayCallback goaway_received_callback =
      +[](absl::string_view) {};
  MoqtSessionTerminatedCallback session_terminated_callback =
      +[](absl::string_view) {};
  MoqtSessionDeletedCallback session_deleted_callback = +[] {};

  MoqtIncomingPublishNamespaceCallback incoming_publish_namespace_callback =
      DefaultIncomingPublishNamespaceCallback;
  MoqtIncomingSubscribeNamespaceCallback incoming_subscribe_namespace_callback =
      DefaultIncomingSubscribeNamespaceCallback;
  const quic::QuicClock* clock = quic::QuicDefaultClock::Get();
};

}  // namespace moqt

#endif  // QUICHE_QUIC_MOQT_MOQT_SESSION_CALLBACKS_H_
